﻿using Common.Infrastructure;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Common.AspNetCore
{
   public class ApiControllerBase : ControllerBase
    {
        private readonly IServiceProvider provider;
        private readonly ILogService log;

        public ApiControllerBase(IServiceProvider provider)
        {
            this.provider = provider; 
            if (provider != null)
            {
                this.log = provider.GetService<ILogService>();
            }
        }

        public IServiceProvider Provider
        {
            get { return this.provider; }
        }

        public ILogService Log
        {
            get { return this.log; }
        }

        public async Task<ApiActionResult<T>> SafeExecuteAsync<T>(Func<ApiActionParam, Task<T>> func)
        {
            ApiActionResult<T> ret = new ApiActionResult<T>();
            try
            {
                ApiActionParam param = new ApiActionParam() { Messages = ret.Messages };
                ret.Data = await func(param);
                ret.Code = param.Code;
                DoAfterSafeExecute();
            }
            catch (CodeException ex)
            {
                var temp = GetErrorMsg(ex, out string trace);
                ret.Messages.Add(new ApiActionMessage() { MessageType = EFMessageType.Error, Message = temp, StackTrace = trace, Code = ex.Code });
            }
            catch (Exception ex)
            {
                var temp = GetErrorMsg(ex, out string trace);
                ret.Messages.Add(new ApiActionMessage() { MessageType = EFMessageType.Error, Message = temp, StackTrace = trace });
            }

            ret.IsSuccess = !ret.Messages.Any(t => t.MessageType == EFMessageType.Error); 
            LibMessageUtils.MessageToActionResult<T>(this.provider, ret);
            if (!ret.IsSuccess && string.IsNullOrEmpty(ret.Code))
            {
                ret.Code = "-1";
            }

            return ret;
        }

        public ApiActionResult<T> SafeExecute<T>(Func<ApiActionParam, T> func)
        {
            ApiActionResult<T> ret = new ApiActionResult<T>();
            try
            {
                ApiActionParam param = new ApiActionParam() { Messages = ret.Messages };
                ret.Data = func(param); 
                ret.Code = param.Code;
                DoAfterSafeExecute();
            }
            catch (CodeException ex)
            {
                var temp = GetErrorMsg(ex, out string trace);
                ret.Messages.Add(new ApiActionMessage() { MessageType = EFMessageType.Error, Message = temp, StackTrace = trace, Code = ex.Code });
            }
            catch (Exception ex)
            {
                var temp = GetErrorMsg(ex, out string trace);
                ret.Messages.Add(new ApiActionMessage() { MessageType = EFMessageType.Error, Message = temp, StackTrace = trace });
            }

            ret.IsSuccess = !ret.Messages.Any(t => t.MessageType == EFMessageType.Error);
            LibMessageUtils.MessageToActionResult<T>(this.provider, ret);
            if (!ret.IsSuccess && string.IsNullOrEmpty(ret.Code))
            {
                ret.Code = "-1";
            }

            return ret;
        }

        public ApiActionResult<T> SafeExecuteByLog<T>(string prefix, bool isSaveOnlyError, object obj, Func<ApiActionParam, T> func)
        {
            var ti = DateTime.Now.Ticks;
            var ret = SafeExecute(func);
            AddSafeLog(prefix, isSaveOnlyError, obj, ret.Data, ret.Messages, ti);
            return ret;
        }

        public async Task<ApiActionResult<T>> SafeExecuteByLogAsync<T>(string prefix, bool isSaveOnlyError, object obj, Func<ApiActionParam, Task<T>> func)
        {
            var ti = DateTime.Now.Ticks;
            var ret = await SafeExecuteAsync(func);
            AddSafeLog(prefix, isSaveOnlyError, obj, ret.Data, ret.Messages, ti);
            return ret;
        }

        #region 信息

        protected void AddError(string message)
        {
            LibMessageUtils.AddError(this.provider, message);
        }

        protected void AddError(string code, params object[] objs)
        {
            LibMessageUtils.AddError(this.provider, code, objs);
        }

        protected void AddWarning(string message)
        {
            LibMessageUtils.AddWarning(this.provider, message);
        }

        protected void AddWarning(string code, params object[] objs)
        {
            LibMessageUtils.AddWarning(this.provider, code, objs);
        }

        protected void AddInfomation(string message)
        {
            LibMessageUtils.AddInfomation(this.provider, message);
        }

        protected void AddInfomation(string code, params object[] objs)
        {
            LibMessageUtils.AddInfomation(this.provider, code, objs);
        }

        protected string GetMessage(string code, params object[] objs)
        {
            return LibMessageUtils.GetMessage(this.provider, code, objs);
        }

        protected void ThrowError(string code, params object[] objs)
        {
            LibMessageUtils.ThrowError(this.provider, code, objs);
        }
        #endregion

        protected string GetErrorMsg(Exception ex, out string trace)
        {
            var error = LibExceptionUtils.GetError(ex); 
            trace = error?.StackTrace;
            return error?.Message;
        }

        protected void DoAfterSafeExecute()
        {
        }

        private void AddSafeLog<T>(string prefix, bool isSaveOnlyError, object obj, T data, List<ApiActionMessage> messages, long startLogTick)
        {
            var mess = string.Empty;
            var error = messages.Where(t => t.MessageType == EFMessageType.Error).ToList();
            if (error.Any())
            {
                mess = string.Join(",", error.Select(t => t.Message + ":" + t.StackTrace));
            }

            if (string.IsNullOrEmpty(mess))
            {
                mess = "OK";
            }

            if (!isSaveOnlyError || error.Any())
            {
                this.Log.AddInfomation(prefix + " " + mess, obj, mess, data, startLogTick);
            }
        }
    }
}
